#pragma once
#include "../Net.hpp"
#include "../Message.hpp"
#include <unordered_map>
/*
模块说明：当服务端接收到一个Rpc请求类型的消息，然后调用回调函数创建一个RpcMessage对象
把消息的正文的各个部分解析RpcMessage对象里面的Json::Value对象中去，从这个Value对象中获取
请求的方法和参数并且判断参数的类型是否准确。
*/

namespace RpcRouter
{
    enum class PraType
    {
        BOOL = 0,
        INT,
        FLOAT,
        STRING,
        ARRAY, // 参数是数组类型
        OBJECT // 参数是对象类型
    };
    // 实际处理业务的回调函数，得到的处理结果通过Value对象返回出去
    using DealRrcCallBack = std::function<void(const Json::Value &, Json::Value &)>;

    // 参数的描述一个枚举值对应一个参数字段描述比如num1字段名称对应的类型是int
    using ParamsDes = std::pair<std::string, PraType>;

    // 再定义一个模块用来检测rpc请求中的参数和返回Rpc回应中的参数是否正确，提供一个服务描述比如说ADD 服务，参数字段是num1,num2
    // 类型都是整形，返回的回应的参数字段名称是result，类型是int,然后提供回调处理rpc请求方法，这个方法处理请求之后还要把结果
    // 带出去
    class SeverDesc
    {
    public:
        // 右值引用直接交互内部资源
        SeverDesc(const std::string &method, DealRrcCallBack &back, std::vector<ParamsDes> &Vparams, PraType return_type)
            : _method(method), _callback(back), _Params_Des(Vparams), _return_type(return_type)
        {
        }
        using ServerDes_ptr = std::shared_ptr<SeverDesc>;

        // 提供一个结果用于参数检测把接收到的rpc请求中的body转成json对象
        // 传入进来和已经注册好的对应的method的参数描述对比一下
        bool CheckParams(const Json::Value &params)
        {
            for (auto &PDes : _Params_Des)
            {
                // 如果参数字段名称不是提前指定好的
                if (params.isMember(PDes.first) == false)
                {
                    DLOG("参数字段名称校验失败，没有该参数名称字段:%s", PDes.first.c_str());
                    return false;
                }
                if(params["method"].asString()=="Div"&&params["nums2"].asInt()==0)
                {
                     DLOG("Div服务除数为0");
                    return false;
                }
                // 如果参数字段对应的值不是提前指定好的类型
                if (IsRightType(PDes.second, params, PDes.first) == false)
                {
                    DLOG("参数字段的值类型校验失败，该参数字段值类型错误:%d", (int)PDes.second);
                    return false;
                }
            }
            return true;
        }

        // 参数结果校验完成后，调用回调函数处理计算task,然后把计算结果和状态码
        // 保存到传入的Value对象汇中，之后构建RpcResponce消息对象时再从该Value
        // 对象中反序列化处理发送给客户端
        bool Call(const Json::Value &params, Json::Value &result)
        {
            _callback(params, result); // 处理计算task，把结果保存在val对象中
            if (ReTypeCheck(result) == false)
            {
                DLOG("reslut字段的值类型校验失败，该参数字段值类型错误");
                return false;
            }
            return true;
        }
        const std::string &GetMethod()
        {
            return _method;
        }

    private:
        bool ReTypeCheck(const Json::Value &val)
        {
            return IsRightType(_return_type, val, "Res");
        }
        // 检测对应的参数字段名称的值是不是指定的类型
        bool IsRightType(PraType type, const Json::Value params, const std::string &namepara)
        {
            switch (type)
            {
            case PraType::BOOL:
                return params[namepara].isBool();
            case PraType::INT:
                return params[namepara].isInt();
            case PraType::FLOAT:
                return params[namepara].isNumeric();
            case PraType::ARRAY:
                return params[namepara].isArray();
            case PraType::STRING:
                return params[namepara].isString();
            case PraType::OBJECT:
                return params[namepara].isObject();
            }
            return false;
        }

    private:
        std::string _method;       // 请求方法
        DealRrcCallBack _callback; // 业务处理回调函数
        std::vector<ParamsDes> _Params_Des;
        PraType _return_type; // 检测完成后返回一个枚举值表示参数的具体类型
    };

    // 定义工厂类来构造SeverDesc对象
    class ServerDescBuild
    {
    public:
        void SetMethodName(const std::string &name)
        {
            _method = name;
        }
        void SetCallBack(const DealRrcCallBack &callback)
        {
            _callback = callback;
        }
        void SetParamsDesc(const ParamsDes &Pd)
        {
            _Params_Des.push_back(Pd);
        }
        void SetReturnType(PraType Ty)
        {
            _return_type = Ty;
        }
        SeverDesc::ServerDes_ptr bulid()
        {
            return std::make_shared<SeverDesc>(_method, _callback, _Params_Des, _return_type);
        }

    private:
        std::string _method;       // 请求方法
        DealRrcCallBack _callback; // 业务处理回调函数
        std::vector<ParamsDes> _Params_Des;
        PraType _return_type; // 检测完成后返回一个枚举值表示参数的具体类型
    };

    // 定义一个管理对象RPC请求中不同的method的方法的参数描述对象
    class ServiceMananger
    {
        // 提供接口管理method的描述对象
    public:
        using ServiceMananger_Ptr = std::shared_ptr<ServiceMananger>;
        // 插入一个Rpc服务描述对象
        void insert(const SeverDesc::ServerDes_ptr &ptr)
        {
            std::unique_lock<std::mutex> lock(_mutex); // RAII加锁自动释放锁
            if (_Method_SDs.count(ptr->GetMethod()) == 0)
            {
                _Method_SDs.insert({ptr->GetMethod(), ptr});
            }
        }
        // 查询一个Rpc服务描述对象
        SeverDesc::ServerDes_ptr select(const std::string &methodname)
        {
            std::unique_lock<std::mutex> lock(_mutex); // RAII加锁自动释放锁
            if (_Method_SDs.count(methodname))
            {
                return _Method_SDs[methodname];
            }
            return SeverDesc::ServerDes_ptr(); // 没有对应的方法的参数描述对象返回一个空对象s
        }
        // 删除一个Rpc参数描述对象
        void Delete(const std::string &methodname)
        {
            std::unique_lock<std::mutex> lock(_mutex); // RAII加锁自动释放锁
            if (_Method_SDs.count(methodname))
            {
                _Method_SDs.erase(methodname);
            }
        }

    private:
        std::mutex _mutex;
        std::unordered_map<std::string, SeverDesc::ServerDes_ptr> _Method_SDs; // 一个method字段对应一个参数描述对象
    };

    // 管理每个方法字段的参数检测对象，参数检测对象中又有实际的业务处理函数
    // 该对象只需要把RpcMessage对象中的正文反序列化到一个Values对象中，然后交给参数检测对象即可
    class Rpc_Ruoter
    {
    public:
        using RpcRouterPtr=std::shared_ptr<Rpc_Ruoter>;
        Rpc_Ruoter() : _Manager(std::make_shared<ServiceMananger>())
        {
        }
        // 处理Rpc请求完成对应的服务功能构造RpcResponce返回给客户端
        void DealRpcQuest(const Abstract::BaseConnection::ConPtr &Con, const Abstract::BaseMessage::MesaPtr &Mptr)
        {
            // 基类智能指针转成派生类智能指针类型
            Message::RpcRequest::RpcPtr RpcMsg = std::dynamic_pointer_cast<Message::RpcRequest>(Mptr);
            // 获取该Rpc请求的uuid
            std::string uuid = RpcMsg->GetUuid();
            // 1 检查该请求中的请求方法字段有没有对应的检测对象SeverDesc
            auto Serverptr = _Manager->select(RpcMsg->GetMethod());
            if (Serverptr.get() == nullptr) // 没有对应的请求服务描述
            {
                DLOG("没有对应的Rpc服务提供");
                return Make_RpcResponce(Con, Json::Value(), ResCode::ResCode_Not_FoundService, uuid);
            }
            // 2 检测Rpc请求里面的参数名称字段和值类型
            if (Serverptr->CheckParams(RpcMsg->GetParams()) == false)
            {
                DLOG("参数校验失败");
                return Make_RpcResponce(Con, Json::Value(), ResCode::ResCode_Invalid_Params, uuid);
            }

            // 3 参数和字段没有问题调用服务名称字段对应的SeverDesc对象进行回调计算
            // 计算的结果和状态码保存到一个Value对象中
            Json::Value result;
            bool ret = Serverptr->Call(RpcMsg->GetParams(), result);
            if (ret == false) // 计算过程出错
            {
                DLOG("服务器计算过程出错");
                return Make_RpcResponce(Con, Json::Value(), ResCode::ResCode_INTERNAL_ERROR, uuid);
            }
            // 4 正确的计算结果并且返回结果
            return Make_RpcResponce(Con, result, ResCode::ResCode_Ok, uuid);
        }
        // 注册接口，提供注册method的参数描述对象
        void RegisterSerDec(const SeverDesc::ServerDes_ptr &ptr)
        {
            _Manager->insert(ptr);
        }

    private:
        // 构造Rpc回应
        void Make_RpcResponce(const Abstract::BaseConnection::ConPtr &Con, const Json::Value &result, ResCode code, std::string &uuid)
        {
            ELOG("进入构造RPC响应模块,状态码:%d", (int)code);
            auto ptr = Message::BuildMessage::build<Message::RpcReponceJson>();
            ptr->Setcode(code);                // 设置正文主体部分
            ptr->SetResult(result);            // 设置正文主体部分
            ptr->SetMtype(Rs_RqType::RPC_Res); // 设置消息的报他头部分
            ptr->SetUuid(uuid);                // 设置消息的报他头部分
            Con->Send(ptr);                    // 发送消息
        }

    private:
        ServiceMananger::ServiceMananger_Ptr _Manager; // 管理不同的method的参数描述对象
    };

}